<?php
// This file is part of Moodle - http://moodle.org/
//
// CalExp is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
/**
 * Build binary tree for Calculate Expressions
 * 
 * @package    questions
 * @category   Calculate Expression
 * @copyright  2014 Quoc Dai
 * @author	   Quoc Dai, Volgograd State Technical University
 * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
 */


/**
* basic node class for build binary tree
*/
abstract class qtype_cppexpression_binary_tree
{
	/** @var string type of node */
	const TYPE = 'undefined';

	/** @var int pos of node's character in expression */
	public $pos;

	/** @var class binary_tree node parent of current node */
	public $parent;

	/** @var class binary_tree node child in left of current node */
	public $childLeft;

	/** @var class binary_tree node child in right of current node */
	public $childRight;

	/**
	* function calculates value of expression
	* @param int $i
	* @param int $j
	* @return int value of expression
	*/
	abstract function calculate_expression($i, $j);

	/**   
	 * get type of node
	 * @return constant value
	 */
	public function get_type_nodes()
	{
		return static::TYPE;
	}

	/**   
	 * reset parent of node
	 * @param node|qtype_cppexpression_binary_tree $new_parent
	 * @return not return
	 */
	public function set_parent($new_parent)
	{
		$this->parent = $new_parent;
	}

	/**   
	 * reset left child of node
	 * @param node|qtype_cppexpression_binary_tree $new_child_left
	 * @return not return
	 */
	public function set_child_left($new_child_left)
	{
		$this->childLeft = $new_child_left;
	}

	/**   
	 * reset right child of node
	 * @param node|qtype_cppexpression_binary_tree $new_child_right
	 * @return not return
	 */
	public function set_child_right($new_child_right)
	{
		$this->childRight = $new_child_right;
	}
}

/**
* 
*/
class qtype_cppexpression_node_plus extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'PLUS';

	function calculate_expression($i, $j) 
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value + $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_sub extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'SUB';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value - $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_mul extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'MUL';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value * $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_div extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'DIV';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		try
		{
			if (!$right_value) 
			{
				$message = 'Can not divide by zero with i = ' . $i . ', j = ' . $j. ', operator DIV on position ' . $this->pos . '<br>';
				throw new Exception($message);
				
			}
			else
			{
				return $left_value / $right_value;	
			}
		}
		catch (Exception $e)
		{
			echo $e->getMessage();
		}
	}
}


/**
* 
*/
class qtype_cppexpression_node_percent extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'PERCENT';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		try
		{
			if (!$right_value) 
			{
				$message = 'Can not divide by zero with i = ' . $i . ', j = ' . $j. ', operator ' . $this->type . ' on position ' . $this->pos . '<br>';
				throw new Exception($message);
				
			}
			else
			{
				return $left_value % (int)$right_value;	
			}
		}
		catch (Exception $e)
		{
			echo $e->getMessage();
		}
	}
}


/**
* 
*/
class qtype_cppexpression_node_greater extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'GREATER';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value > $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_less extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'LESS';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value < $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_greater_or_equal extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'GREATER_OR_EQUAL';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value >= $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_less_or_equal extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'LESS_OR_EQUAL';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value <= $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_equal extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'EQUAL';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value === $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_different extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'DIFFERENT';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value !== $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_and extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'AND';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value && $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_or extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'OR';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value || $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_not extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'NOT';

	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		
		return !$left_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_parameter extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'PARAMETER';

	/** @var double data of node */
	public $data;
	public $type;

	function __construct($name_parameters = NULL)
	{
		if ($name_parameters == 'i' || $name_parameters == 'I') 
		{
			$this->type = 'I';
		}
		else if ($name_parameters == 'j' || $name_parameters == 'J') 
		{
			$this->type = 'J';
		}
		else
		{
			$this->type = 'NUMBER';
		}
	}

	function calculate_expression($i, $j)
	{		
		if ( $this->type == 'I') 
		{
			return $i;
		}
		else if( $this->type == 'J')
		{
			return $j;
		}
		else
		{
			return $this->data;
		}
	}
}


/**
* 
*/
class qtype_cppexpression_node_or_bit extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'OR_BIT';
	
	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value | $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_and_bit extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'AND_BIT';
	
	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value & $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_xor_bit extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'XOR_BIT';
	
	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value ^ $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_not_bit extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'NOT_BIT';
	
	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		
		return ~$left_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_left_shift extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'LEFT_SHIFT';
	
	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value << $right_value;
	}
}


/**
* 
*/
class qtype_cppexpression_node_right_shift extends qtype_cppexpression_binary_tree
{
	
	const TYPE = 'RIGHT_SHIFT';
	
	function calculate_expression($i, $j)
	{
		$left_value = $this->childLeft->calculate_expression($i, $j);
		$right_value = $this->childRight->calculate_expression($i, $j);
		
		return $left_value >> $right_value;
	}
}

?>